import type { Plugin } from "vite";
import fs from "fs";
import path from "path";
import postcss from "postcss";
import autoprefixer from "autoprefixer";
import precss from "precss";
import { parse, SFCParseResult } from "@vue/compiler-sfc";
import { TemplateChildNode } from "@vue/compiler-core";
import Result from "postcss/lib/result";
export default function Purifycss(params?: { cssList?: string[] }): Plugin {
  let fileTag: string[] = [];
  let cssTag: string[] = [];
  let arrList: string[] = [];
  let againstArrList: string[] = [];
  let cssIDList: string[] = [];
  let cssFileInner: string[] = [];
  return {
    name: "vite-plugin-vue-purifycss",
    apply: "build",
    enforce: "post",
    buildStart() {},
    transform(code, id, opt) {
      getFileTag(code, id, cssTag, fileTag, cssIDList);
    },
    async generateBundle(_, bundle) {
      await getCssTag(
        params?.cssList || [],
        cssIDList,
        cssTag,
        fileTag,
        arrList,
        againstArrList,
        cssFileInner
      );
      // 遍历bundle
      for (const key in bundle) {
        if (bundle[key]) {
          const chunk = bundle[key]; // 拿到文件名对应的值

          // 判断+提取+移除
          if (chunk.type == "asset" && chunk.fileName.includes(".css")) {
            // console.log("chunk", chunk);
            await postcss([precss, autoprefixer]) // [ autoprefixer] 为使用的插件列表，返回 Processor 对象
              .process(chunk.source) // process 接收 css 资源
              .then((result) => {
                const res = result.root;
                cssTag = [...new Set(cssTag)];
                againstArrList = [...new Set(againstArrList)];
                // console.log("againstArrList", againstArrList);
                for (let i of againstArrList) {
                  for (let n of res.nodes as any) {
                    if (n.selector && n.selector == i) {
                      n.remove();
                    }
                  }
                }
                chunk.source = res.toResult().css;
              });
          }
        }
      }
    },
    closeBundle() {},
  };
}

// 选取有用的css规则 剔除不必要的css样式
const handleCssRules = (
  fileTag: string[],
  cssTag: string[],
  arrList: string[],
  againstArrList: string[]
) => {
  for (let i of fileTag) {
    for (let k of cssTag) {
      //默认取整个字符串作对比选择器
      let sliceString = i.slice(0);
      //引入的css或者scss文件的选择器截取 不要data-v-xxxx后面的识别字符串
      if (!k.includes("[data-v-") && i.includes("[data-v-")) {
        sliceString = i.slice(0, i.indexOf("[data-v-"));
      }
      if (
        k.includes(sliceString) &&
        k.indexOf(sliceString) + sliceString.length == k.length
      ) {
        arrList.push(k);
      } else if (
        k.includes(sliceString) &&
        k.includes(" ") &&
        k.slice(
          k.indexOf(sliceString) + sliceString.length + 1,
          k.indexOf(sliceString) + sliceString.length + 2
        ) == ":"
      ) {
        arrList.push(k);
      } else if (
        k.includes(sliceString) &&
        !k.includes(" ") &&
        k.slice(
          k.indexOf(sliceString) + sliceString.length,
          k.indexOf(sliceString) + sliceString.length + 1
        ) == ":"
      ) {
        arrList.push(k);
      } else if (
        k.includes(sliceString) &&
        k.includes(" ") &&
        k.slice(
          k.indexOf(sliceString) + sliceString.length + 1,
          k.indexOf(sliceString) + sliceString.length + 2
        ) !== "." &&
        k.slice(
          k.indexOf(sliceString) + sliceString.length + 1,
          k.indexOf(sliceString) + sliceString.length + 2
        ) !== "#" &&
        k.slice(k.lastIndexOf(" ") + 1, k.lastIndexOf(" ") + 2) !== "." &&
        k.slice(k.lastIndexOf(" ") + 1, k.lastIndexOf(" ") + 2) !== "#"
      ) {
        arrList.push(k);
      } else if (
        k.includes(sliceString) &&
        k.includes(",") &&
        !k.includes(" ")
      ) {
        let joinArrFlag = false;
        k.split(",").forEach((item) => {
          if (item == sliceString) {
            joinArrFlag = true;
          }
        });
        if (joinArrFlag) arrList.push(k);
      } else if (
        k.includes(sliceString) &&
        k.includes(",") &&
        k.includes(" ")
      ) {
        let joinArrFlag = false;
        k.split(",").forEach((item) => {
          if (item.slice(item.lastIndexOf(" ") + 1) == sliceString) {
            joinArrFlag = true;
          }
        });
        if (joinArrFlag) arrList.push(k);
      } else if (!k.includes("#") && !k.includes(".")) {
        arrList.push(k);
      } else if (k.includes(":deep(")) {
        arrList.push(k);
      } else if (k.includes("::v-deep")) {
        arrList.push(k);
      } else if (k.includes(".el-")) {
        arrList.push(k);
      } else if (k.includes(".ant-")) {
        arrList.push(k);
      } else if (k.includes(">") || k.includes("~") || k.includes("+")) {
        arrList.push(k);
      }
    }
  }
  arrList = [...new Set(arrList)];
  let keyObj = <
    {
      [key: string]: boolean;
    }
  >{};
  arrList.forEach((i) => {
    keyObj[`${i}`] = true;
  }); // 将需要对比的数组的值作为obj的key
  cssTag.forEach((i) => {
    if (!keyObj[i]) againstArrList.push(i);
  }); // 这里是对比出来不同的元素
  againstArrList = [...new Set(againstArrList)];
  // console.log("cssTag", cssTag);
  // console.log("fileTag", fileTag);
  // console.log("arrList", arrList);
  // console.log("againstArrList", againstArrList);
};

// 处理样式名
const handleCssFile = (
  result: Result,
  cssTag: string[],
  fileTag: string[],
  arrList: string[],
  againstArrList: string[]
) => {
  const root = result.root;
  root.nodes.forEach((i: any) => {
    if (i.selector) {
      i.selector = i.selector
        .replace(/(\r\n)/gi, "")
        .replace('[dir="ltr"] ', "")
        .replace('[dir="rtl"] ', "")
        .replace(/(, )/gi, ",")
        .replace(/(::)/gi, ":")
        .replace(/(     )/gi, "")
        .replace(/(  )/gi, "");
      cssTag.push(i.selector);
    }
  });
  cssTag = [...new Set(cssTag)];
  handleCssRules(fileTag, cssTag, arrList, againstArrList);
};

const handleVueCssFile = (code: string, result: Result, cssTag: string[]) => {
  const root = result.root;
  root.nodes.forEach((i: any) => {
    if (i.selector) {
      i.selector = i.selector
        .replace(/(\r\n)/gi, "")
        .replace('[dir="ltr"] ', "")
        .replace('[dir="rtl"] ', "")
        .replace(/(, )/gi, ",")
        .replace(/(::)/gi, ":")
        .replace(/(     )/gi, "")
        .replace(/(  )/gi, "");
      if (i.selector.includes(" :")) {
        if (i.selector.includes(",")) {
          let is = i.selector.split(", ").map((item: string) => {
            if (code.includes("__scopeId")) {
              return (item =
                item.slice(0, item.indexOf(" :")) +
                "[" +
                code.slice(
                  code.indexOf("__scopeId") + 13,
                  code.indexOf("__scopeId") + 28
                ) +
                "]" +
                item.slice(item.indexOf(" :")));
            } else {
              return item;
            }
          });
          i.selector = is.join(",");
        } else {
          if (code.includes("__scopeId")) {
            i.selector =
              i.selector.slice(0, i.selector.indexOf(" :")) +
              "[" +
              code.slice(
                code.indexOf("__scopeId") + 13,
                code.indexOf("__scopeId") + 28
              ) +
              "]" +
              i.selector.slice(i.selector.indexOf(" :"));
          }
        }
      } else if (i.selector.includes(":")) {
        if (i.selector.includes(",")) {
          let is = i.selector.split(", ").map((item: string) => {
            if (code.includes("__scopeId")) {
              return (item =
                item.slice(0, item.indexOf(":")) +
                "[" +
                code.slice(
                  code.indexOf("__scopeId") + 13,
                  code.indexOf("__scopeId") + 28
                ) +
                "]" +
                item.slice(item.indexOf(":")));
            } else {
              return item;
            }
          });
          i.selector = is.join(",");
        } else {
          if (code.includes("__scopeId")) {
            i.selector =
              i.selector.slice(0, i.selector.indexOf(":")) +
              "[" +
              code.slice(
                code.indexOf("__scopeId") + 13,
                code.indexOf("__scopeId") + 28
              ) +
              "]" +
              i.selector.slice(i.selector.indexOf(":"));
          }
        }
      } else {
        if (code.includes("__scopeId")) {
          i.selector =
            i.selector +
            "[" +
            code.slice(
              code.indexOf("__scopeId") + 13,
              code.indexOf("__scopeId") + 28
            ) +
            "]";
        }
      }
      cssTag.push(i.selector);
    }
  });
};

//获取文件中的样式名
const getClassID = (
  children: TemplateChildNode[],
  tag: string[],
  code: string
) => {
  children.forEach((item: any) => {
    if (item.props) {
      if (item.props.length == 0) {
        getClassID(item.children, tag, code);
      } else {
        for (let i of item.props) {
          if (i.name == "id") {
            let arr = i.value.content.split(" ");
            for (let a of arr) {
              if (code.includes("__scopeId")) {
                tag.push(
                  `#${a}[${code.slice(
                    code.indexOf("__scopeId") + 13,
                    code.indexOf("__scopeId") + 28
                  )}]`
                );
              } else {
                tag.push(`#${a}`);
              }
            }
            getClassID(item.children, tag, code);
          } else if (i.name == "class") {
            let arr = i.value.content.split(" ");
            for (let a of arr) {
              if (code.includes("__scopeId")) {
                tag.push(
                  `.${a}[${code.slice(
                    code.indexOf("__scopeId") + 13,
                    code.indexOf("__scopeId") + 28
                  )}]`
                );
              } else {
                tag.push(`.${a}`);
              }
            }
            getClassID(item.children, tag, code);
          } else if (
            i.name == "bind" &&
            i.arg &&
            (i.arg.content == "class" || i.arg.content == "id")
          ) {
            let indexList: number[] = [];
            let newIndexList: number[] = [];
            getIndexList(i, indexList);
            for (let k = 0; k < indexList.length; k++) {
              if (newIndexList.indexOf(indexList[k]) == -1) {
                newIndexList.push(indexList[k]);
              } else {
                newIndexList.pop();
              }
            }
            for (let k = 0; k < newIndexList.length; k = k + 2) {
              if (code.includes("__scopeId")) {
                tag.push(
                  `${i.arg.content == "class" ? "." : "#"}${i.exp.content.slice(
                    newIndexList[k],
                    newIndexList[k + 1]
                  )}[${code.slice(
                    code.indexOf("__scopeId") + 13,
                    code.indexOf("__scopeId") + 28
                  )}]`
                );
              } else {
                tag.push(
                  `${i.arg.content == "class" ? "." : "#"}${i.exp.content.slice(
                    newIndexList[k],
                    newIndexList[k + 1]
                  )}`
                );
              }
            }
            getClassID(item.children, tag, code);
          }
        }
      }
    }
  });
};

//当class是动态绑定的时候 获取''单引号的index组成的indexList
const getIndexList = (i: any, indexList: number[]) => {
  let index = indexList[indexList.length == 0 ? 0 : indexList.length - 1]
    ? indexList[indexList.length == 0 ? 0 : indexList.length - 1]
    : 0;
  if (
    indexList.length % 2 == 0 &&
    i.exp.content.indexOf("'", index == 0 ? index : index + 1) !== -1
  ) {
    indexList.push(
      i.exp.content.indexOf("'", index == 0 ? index : index + 1) + 1
    );
  } else if (indexList.length % 2 == 1) {
    indexList.push(i.exp.content.indexOf("'", index));
  }
  if (i.exp.content.indexOf("'", index == 0 ? index : index + 1) !== -1) {
    getIndexList(i, indexList);
  }
};

const getFileTag = async (
  code: string,
  id: string,
  cssTag: string[],
  fileTag: string[],
  cssIDList: string[]
) => {
  if (!id.includes("?")) {
    if (id.includes(".vue")) {
      const src = path.resolve(id);
      const text = fs.readFileSync(src, "utf-8");
      let SFCParseResult = parse(text) as SFCParseResult;
      getClassID(
        SFCParseResult.descriptor.template!.ast.children,
        fileTag,
        code
      );
      fileTag = [...new Set(fileTag)];
      //获取vue文件中style的css的样式
      if (
        SFCParseResult.descriptor.styles.length !== 0 &&
        !id.includes("App.vue")
      ) {
        let content = SFCParseResult.descriptor.styles[0].content
          .replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, "\n") //多行注释
          .replace(/\/\/.*/g, ""); //单行注释
        await postcss([precss, autoprefixer]) // [ autoprefixer] 为使用的插件列表，返回 Processor 对象
          .process(content) // process 接收 css 资源
          .then((result) => {
            handleVueCssFile(code, result, cssTag);
          });
      }
    } else if (
      id.includes(".css") ||
      id.includes(".scss") ||
      id.includes(".less")
    ) {
      cssIDList.push(id);
    }
  }
};

const handleLoopEvent = async (
  id: string,
  cssTag: string[],
  fileTag: string[],
  arrList: string[],
  againstArrList: string[],
  cssFileInner: string[]
) => {
  const src = path.resolve(id);
  let text = fs
    .readFileSync(src, "utf-8")
    .replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, "\n") //多行注释
    .replace(/\/\/.*/g, ""); //单行注释
  let regex = /\@import (.+?)\;/g;
  let result: any;
  while ((result = regex.exec(text)) != null) {
    cssFileInner.push(
      path.resolve(
        src.slice(0, id.lastIndexOf("/")),
        result[1].slice(
          result[1].includes('"')
            ? result[1].indexOf('"') + 1
            : result[1].indexOf("'"),
          result[1].includes('"')
            ? result[1].lastIndexOf('"')
            : result[1].includes('"')
        )
      )
    );
  }
  text = text.replace(/(?:^|\n|\r)\s*\@import.*(?:\r|\n|$)/g, "\n");
  await postcss([precss, autoprefixer]) // [ autoprefixer] 为使用的插件列表，返回 Processor 对象
    .process(text) // process 接收 css 资源
    .then((result) => {
      handleCssFile(result, cssTag, fileTag, arrList, againstArrList);
    });
};

//获取css文本
const getCssTag = async (
  cssList: string[],
  cssIDList: string[],
  cssTag: string[],
  fileTag: string[],
  arrList: string[],
  againstArrList: string[],
  cssFileInner: string[]
) => {
  if (cssList.length == 0) {
    handleCssRules(fileTag, cssTag, arrList, againstArrList);
  }
  for (let item of cssList) {
    for (let id of cssIDList) {
      if (id.includes(`/${item}`)) {
        await handleLoopEvent(
          id,
          cssTag,
          fileTag,
          arrList,
          againstArrList,
          cssFileInner
        );
      }
    }
  }
  for (let i of cssFileInner) {
    await handleLoopEvent(
      i,
      cssTag,
      fileTag,
      arrList,
      againstArrList,
      cssFileInner
    );
  }
};
